//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using EffiProzDB.RowIO;
using EffiProzDB.Lib;


namespace EffiProzDB.ScriptIO
{

    /**
     * Handles operations involving reading back a script or log file written
     * _out by ScriptWriterText. This implementation
     * corresponds to ScriptWriterText.
     *
     *  @author fredt@users
     *  @version 1.8.0
     *  @since 1.7.2
     */
    public class ScriptReaderText : ScriptReaderBase
    {

        // this is used only to enable reading one logged line at a time
        StreamReader dataStreamIn;
        RowInputTextLog rowIn;
        bool isInsert;

        public ScriptReaderText(Database db,
                         string file)
            : base(db, file)
        {

            rowIn = new RowInputTextLog();
        }

        protected override void openFile()
        {

            Stream d = db.getFileAccess().OpenInputStreamElement(fileName);

            dataStreamIn = new StreamReader(d);
        }

        protected override void readDDL(Session session)
        {

            for (; readLoggedStatement(session); )
            {
                if (rowIn.getStatementType() == INSERT_STATEMENT)
                {
                    isInsert = true;

                    break;
                }

                Result result = session.sqlExecuteDirectNoPreChecks(statement);

                if (result != null && result.isError())
                {
                    db.logger.appLog.logContext(SimpleLog.LOG_ERROR,
                                                result.getMainString());

                    HsqlException error =
                        Trace.error(Trace.ERROR_IN_SCRIPT_FILE,
                                    Trace.DatabaseScriptReader_readDDL,
                                    new Object[] {
                      (lineCount), result.getMainString()
                });

                    /** @todo fredt - if unavaialble external functions are to be ignored */
                    throw error;
                }
            }
        }

        protected override void readExistingData(Session session)
        {

            try
            {
                string tablename = null;

                // fredt - needed for forward referencing FK constraints
                db.setReferentialIntegrity(false);

                for (; isInsert || readLoggedStatement(session);
                        isInsert = false)
                {
                    if (statementType == SCHEMA_STATEMENT)
                    {
                        session.setSchema(currentSchema);

                        continue;
                    }
                    else if (statementType == INSERT_STATEMENT)
                    {
                        if (!rowIn.getTableName().Equals(tablename))
                        {
                            tablename = rowIn.getTableName();

                            string schema = session.getSchemaName(currentSchema);

                            currentTable = db.schemaManager.getUserTable(session,
                                    tablename, schema);
                        }

                        currentTable.insertFromScript(rowData);
                    }
                }

                db.setReferentialIntegrity(true);
            }
            catch (Exception e)
            {
                db.logger.appLog.logContext(e, null);

                throw Trace.error(Trace.ERROR_IN_SCRIPT_FILE,
                                  Trace.DatabaseScriptReader_readExistingData,
                                  new Object[] {
                  (lineCount), e.ToString()
            });
            }
        }

        public override bool readLoggedStatement(Session session)
        {

            
            //fredt temporary solution - should read bytes directly from buffer
            string s = dataStreamIn.ReadLine();

            lineCount++;

            //         Console.WriteLine(lineCount);
            statement = StringConverter.asciiToUnicode(s);

            if (statement == null)
            {
                return false;
            }

            processStatement(session);

            return true;
        }

        private void processStatement(Session session)
        {

            try
            {
                if (statement.StartsWith("/*C"))
                {
                    int endid = statement.IndexOf('*', 4);

                    sessionNumber = int.Parse(statement.Substring(3,
                            endid-3));
                    statement = statement.Substring(endid + 2);
                }

                rowIn.setSource(statement);

                statementType = rowIn.getStatementType();

                if (statementType == ANY_STATEMENT)
                {
                    rowData = null;
                    currentTable = null;

                    return;
                }
                else if (statementType == COMMIT_STATEMENT)
                {
                    rowData = null;
                    currentTable = null;

                    return;
                }
                else if (statementType == SCHEMA_STATEMENT)
                {
                    rowData = null;
                    currentTable = null;
                    currentSchema = rowIn.getSchemaName();

                    return;
                }

                string name = rowIn.getTableName();
                string schema = session.getSchemaName(null);

                currentTable = db.schemaManager.getUserTable(session, name,
                        schema);

                int[] colTypes;

                if (statementType == INSERT_STATEMENT)
                {
                    colTypes = currentTable.getColumnTypes();
                }
                else if (currentTable.hasPrimaryKey())
                {
                    colTypes = currentTable.getPrimaryKeyTypes();
                }
                else
                {
                    colTypes = currentTable.getColumnTypes();
                }

                rowData = rowIn.readData(colTypes);
            }
            catch (Exception e)
            {
                throw new IOException(e.ToString());
            }
        }

        public override void close()
        {

            try
            {
                dataStreamIn.Close();
            }
            catch (Exception ) { }
        }
    }
}
